home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Xconq 7.1.0 / src / xconq-7.1.0 / mac / macmap2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-07  |  25.4 KB  |  1,068 lines  |  [TEXT/R*ch]

  1. /* Map interaction for the Mac interface to Xconq.
  2.    Copyright (C) 1992, 1993, 1994, 1995, 1996 Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. #include "conq.h"
  10. #include "macconq.h"
  11. extern void do_add_terrain(void);
  12. extern void do_remove_terrain(void);
  13. extern void do_set_formation(void);
  14. extern void set_position_modally(void);
  15.  
  16. static pascal void map_scroll_fn(ControlHandle control, short code);
  17.  
  18. int topunithgt = 60;
  19.  
  20. /* This is a temporary used by the map scroll proc. */
  21.  
  22. Map *curmap;
  23.  
  24. ControlActionUPP map_scroll_proc;
  25.  
  26. /* This scroll proc is shared by both the horizontal and vertical scrollbars. */
  27.  
  28. static pascal void
  29. map_scroll_fn(ControlHandle control, short code)
  30. {
  31.     int curvalue, pagesize, jump;
  32.     int oldsx, oldsy;
  33.     RgnHandle tmprgn;
  34.     Rect tmprect;
  35.  
  36.     /* The page jump should be most but not all of a screenful. */
  37.     if (control == curmap->hscrollbar) {
  38.         pagesize = (3 * curmap->vp->pxw) / 4;
  39.     } else {
  40.         pagesize = (3 * curmap->vp->pxh) / 4;
  41.     }
  42.     /* Adjust the pagesize to always be a multiple of 4. */
  43.     pagesize = ((pagesize + 3) / 4) * 4;
  44.     switch (code) {
  45.         case inPageDown:
  46.             jump = pagesize;
  47.             break;
  48.         case inDownButton:
  49.             jump = 4;
  50.             break;
  51.         case inPageUp:
  52.             jump = 0 - pagesize;
  53.             break;
  54.         case inUpButton:
  55.             jump = -4;
  56.             break;
  57.         default:
  58.             jump = 0;
  59.             break;
  60.     }
  61.     /* Letting control's code do the max/min hacking. */
  62.     SetCtlValue(control, GetCtlValue(control) + jump);
  63.     curvalue = GetCtlValue(control);
  64.     /* Tweak the map's own variables to match. */
  65.     oldsx = curmap->vp->sx;  oldsy = curmap->vp->sy;
  66.     if (control == curmap->hscrollbar) {
  67.         set_view_position(curmap->vp, curvalue, curmap->vp->sy);
  68.     } else {
  69.         set_view_position(curmap->vp, curmap->vp->sx, curvalue);
  70.     }
  71.     m_focus_on_center(curmap);
  72.     set_content_rect(curmap);
  73.     /* Scroll the already-drawn bits. */
  74.     tmprgn = NewRgn();
  75.     SetRect(&tmprect, conwid, curmap->toph, conwid + curmap->vp->pxw, curmap->toph + curmap->vp->pxh);
  76.     ScrollRect(&tmprect, oldsx - curmap->vp->sx, oldsy - curmap->vp->sy, tmprgn);
  77.     InvalRgn(tmprgn);
  78.     /* Do the update now, because we won't get back to the main event loop
  79.        until the mouse button is released. */
  80.     update_window(curmap->window);
  81.     DisposeRgn(tmprgn);
  82. }
  83.  
  84. /* Handle a mouse down in the map window. */
  85.  
  86. void
  87. do_mouse_down_map(Map *map, Point mouse, int mods)
  88. {
  89.     ControlHandle control;
  90.     short part;
  91.     int oldsx = map->vp->sx, oldsy = map->vp->sy;
  92.     WindowPtr window = map->window;
  93.     RgnHandle tmprgn;
  94.     Rect tmprect;
  95.  
  96.     if (map_scroll_proc == NULL)
  97.       map_scroll_proc = NewControlActionProc(map_scroll_fn);
  98.  
  99.     part = FindControl(mouse, window, &control);
  100.     if (control == map->hscrollbar) {
  101.         /* Handle the horizontal scrollbar. */
  102.         switch (part) {
  103.             case inThumb:
  104.                 /* (should add power thumb ability?) */
  105.                 part = TrackControl(control, mouse, NULL);
  106.                 if (part == inThumb) {
  107.                     map->vp->sx = GetCtlValue(control);
  108.                     if (oldsx != map->vp->sx) {
  109.                         m_focus_on_center(map);
  110.                         set_content_rect(map);
  111.                         /* Scroll the already-drawn bits. */
  112.                         tmprgn = NewRgn();
  113.                         SetRect(&tmprect, conwid, map->toph, conwid + map->vp->pxw, map->toph + map->vp->pxh);
  114.                         ScrollRect(&tmprect, oldsx - map->vp->sx, oldsy - map->vp->sy, tmprgn);
  115.                         InvalRgn(tmprgn);
  116.                         DisposeRgn(tmprgn);
  117.                         draw_related_maps(map);
  118.                     }
  119.                 }
  120.                 break;
  121.             default:
  122.                 curmap = map;
  123.                 part = TrackControl(control, mouse, map_scroll_proc);
  124.                 break;
  125.         }
  126.     } else if (control == map->vscrollbar) {
  127.         /* Handle the vertical scrollbar. */
  128.         switch (part) {
  129.             case inThumb:
  130.                 /* (should add power thumb ability?) */
  131.                 part = TrackControl(control, mouse, NULL);
  132.                 if (part == inThumb) {
  133.                     map->vp->sy = GetCtlValue(control);
  134.                     if (oldsy != map->vp->sy) {
  135.                         m_focus_on_center(map);
  136.                         set_content_rect(map);
  137.                         /* Scroll the already-drawn bits. */
  138.                         tmprgn = NewRgn();
  139.                         SetRect(&tmprect, conwid, map->toph, conwid + map->vp->pxw, map->toph + map->vp->pxh);
  140.                         ScrollRect(&tmprect, oldsx - map->vp->sx, oldsy - map->vp->sy, tmprgn);
  141.                         InvalRgn(tmprgn);
  142.                         DisposeRgn(tmprgn);
  143.                         draw_related_maps(map);
  144.                     }
  145.                 }
  146.                 break;
  147.             default:
  148.                 curmap = map;
  149.                 part = TrackControl(control, mouse, map_scroll_proc);
  150.                 break;
  151.         }
  152.     } else if (mouse.h <= conwid) {
  153.         /* Interpret as a control panel hit. */
  154.         do_mouse_down_map_control_panel(map, mouse.h, mouse.v, mods);
  155.     } else {
  156.         do_mouse_down_map_content(map, mouse.h, mouse.v, mods);
  157.     }
  158. }
  159.  
  160. void
  161. do_mouse_down_map_control_panel(Map *map, int h, int v, int mods)
  162. {
  163.     int winh = map->window->portRect.bottom - map->window->portRect.top;
  164.  
  165.     /* (should better organize tests here) */
  166.     if (between(winh - 2 * sbarwid, v, winh)) {
  167.         switch ((winh - v) / sbarwid) {
  168.             case 0:
  169.                 magnify_map(map, ((h < conwid / 2) ? -1 : 1));
  170.                 break;
  171. #if 0
  172.             /* This was too confusing here, so it's now flushed.  However, the
  173.                capability still seems worthwhile, so it should reappear elsewhere. */
  174.             case 1:
  175.                 map_modal = ZOOM_MODAL;
  176.                 break;
  177. #endif
  178.         }
  179.     } else if (v < 32) {
  180.         toggle_survey(map);
  181.     } else if ((v - 32) < 5 * 15) {
  182.         switch ((v - 32) / 15) {
  183.             case 0:
  184.                 if (h < conwid / 2) {
  185.                     select_previous_awake_mover(map);
  186.                 } else {
  187.                     select_next_awake_mover(map);
  188.                 }
  189.                 break;
  190.             case 1:
  191.                 if (h < conwid / 2) {
  192.                     select_previous_mover(map);
  193.                 } else {
  194.                     select_next_mover(map);
  195.                 }
  196.                 break;
  197.             case 2:
  198.                 if (h < conwid / 2) {
  199.                     select_previous_actor(map);
  200.                 } else {
  201.                     select_next_actor(map);
  202.                 }
  203.                 break;
  204.             case 3:
  205.                 if (h < conwid / 2) {
  206.                     select_previous_unit(map);
  207.                 } else {
  208.                     select_next_unit(map);
  209.                 }
  210.                 break;
  211.             case 4:
  212.                 beep();
  213.                 break;
  214.         }
  215.     } else if (v - 32 - 5*15 - 2 - 5/*why?*/ < 6 * 11) {
  216.         switch ((v - 32 - 5*15 - 2 - 5/*why?*/) / 11) {
  217.             case 0:
  218.                 toggle_map_grid(map);
  219.                 break;
  220.             case 1:
  221.                 toggle_map_names(map);
  222.                 break;
  223.             case 2:
  224.                 if (people_sides_defined()) {
  225.                     toggle_map_people(map);
  226.                 }
  227.                 break;
  228.             case 3:
  229.                 toggle_map_plans(map);
  230.                 break;
  231.             case 4:
  232.                 toggle_map_ai(map);
  233.                 break;
  234.             case 5:
  235.                 if (dside->may_set_see_all) {
  236.                     dside->see_all = !dside->see_all;
  237.                     calc_vision(dside);
  238.                     force_map_update(map);
  239.                 }
  240.                 break;
  241.         }
  242.     } else {
  243.         /* Unused area, ignore */
  244.     }
  245. }
  246.  
  247. void
  248. toggle_survey(Map *map)
  249. {
  250.     int i;
  251.     Unit *unit;
  252.  
  253.     map->moveonclick = !map->moveonclick;
  254.     map->autoselect = !map->autoselect;
  255.     draw_control_panel(map);
  256.     if (map->autoselect) {
  257.         if (map->numselections > 0) {
  258.             for (i = 0; i < map->numselections; ++i) {
  259.                 unit = map->selections[i];
  260.                 if (unit != NULL) {
  261.                     map->curunit = autonext_unit(dside, unit);
  262.                     select_exactly_one_unit(map, map->curunit);
  263.                 }
  264.             }
  265.         }
  266.     }
  267. }
  268.  
  269. void
  270. magnify_map(Map *map, int inout)
  271. {
  272.     set_map_mag(map, map->vp->power + inout);
  273. }
  274.  
  275. /* This sets the map's magnification directly and updates it. */
  276.  
  277. void
  278. set_map_mag(Map *map, int newpower)
  279. {
  280.     newpower = clip_to_limits(0, newpower, NUMPOWERS-1);
  281.     if (map->vp->power != newpower) {
  282.         set_map_power(map, newpower);
  283.         m_center_on_focus(map);
  284.         set_map_scrollbars(map);
  285.         force_map_update(map);
  286.         draw_related_maps(map);
  287.     }
  288. }
  289.  
  290. void
  291. toggle_map_grid(Map *map)
  292. {
  293.     map->drawgrid = !map->drawgrid;
  294.     /* (should not do a total redraw?) */
  295.     force_map_update(map);
  296. }
  297.  
  298. void
  299. toggle_map_topline(Map *map)
  300. {
  301.     int oldtoph;
  302.     Rect tmprect;
  303.     RgnHandle tmprgn;
  304.     GrafPtr oldport;
  305.  
  306.     oldtoph = map->toph;
  307.     map->toplineh = (map->toplineh ? 0 : tophgt);
  308.     map->toph = map->toplineh + map->topunith;
  309.     set_content_rect(map);
  310.     if (map->toplineh) {
  311.          GetPort(&oldport);
  312.         SetPort(map->window);
  313.         tmprgn = NewRgn();
  314.         tmprect = map->window->portRect;
  315.         tmprect.left += conwid;
  316.         tmprect.right -= sbarwid;  tmprect.bottom -= sbarwid;
  317.         ScrollRect(&tmprect, 0, map->toplineh, tmprgn);
  318.         draw_top_line(map);
  319.         InvalRgn(tmprgn);
  320.         update_window(map->window);
  321.         DisposeRgn(tmprgn);
  322.         SetPort(oldport);
  323.     } else {
  324.         force_map_update(map);
  325.     }
  326. }
  327.  
  328. void
  329. toggle_map_topunit(Map *map)
  330. {
  331.     int oldtoph;
  332.     Rect tmprect;
  333.     RgnHandle tmprgn;
  334.     GrafPtr oldport;
  335.  
  336.     oldtoph = map->toph;
  337.     map->topunith = (map->topunith ? 0 : topunithgt);
  338.     map->toph = map->toplineh + map->topunith;
  339.     set_content_rect(map);
  340.     if (map->topunith) {
  341.          GetPort(&oldport);
  342.         SetPort(map->window);
  343.         tmprect = map->window->portRect;
  344.         tmprect.left += conwid;  tmprect.top += oldtoph;
  345.         tmprect.right -= sbarwid;  tmprect.bottom -= sbarwid;
  346.         tmprgn = NewRgn();
  347.         ScrollRect(&tmprect, 0, map->topunith, tmprgn);
  348.         InvalRgn(tmprgn);
  349.         update_window(map->window);
  350.         DisposeRgn(tmprgn);
  351.         draw_unit_info(map);
  352.         SetPort(oldport);
  353.     } else {
  354.         force_map_update(map);
  355.     }
  356. }
  357.  
  358. void
  359. toggle_map_other_maps(Map *map)
  360. {
  361.     map->drawothermaps = !map->drawothermaps;
  362.     /* (should not do a total redraw) */
  363.     force_map_update(map);
  364. }
  365.  
  366. void
  367. toggle_map_lighting(Map *map)
  368. {
  369.     map->drawlighting = !map->drawlighting;
  370.     /* We have to do a total redraw. */
  371.     force_map_update(map);
  372. }
  373.  
  374. void
  375. toggle_map_coverage(Map *map)
  376. {
  377.     map->drawcover = !map->drawcover;
  378.     /* (should only change newly dimmed/lightened cells) */
  379.     force_map_update(map);
  380. }
  381.  
  382. void
  383. toggle_map_names(Map *map)
  384. {
  385.     map->drawnames = !map->drawnames;
  386.     /* (if now on, should draw names on top of everything, don't redraw everything) */
  387.     if (map->vp->hh > 5) {
  388.         force_map_update(map);
  389.     } else {
  390.         /* (should be a force update on control panel alone) */
  391.         draw_control_panel(map);
  392.     }
  393. }
  394.  
  395. void
  396. toggle_map_people(Map *map)
  397. {
  398.     map->drawpeople = !map->drawpeople;
  399.     if (bwid2[map->vp->power] > 0) {
  400.         force_map_update(map);
  401.     } else {
  402.         /* (should be a force update on control panel alone) */
  403.         draw_control_panel(map);
  404.     }
  405. }
  406.  
  407. void
  408. toggle_map_elevations(Map *map)
  409. {
  410.     map->drawelevations = !map->drawelevations;
  411.     force_map_update(map);
  412. }
  413.  
  414. void
  415. toggle_map_materials(Map *map, int m)
  416. {
  417.     map->drawmaterials[m] = !map->drawmaterials[m];
  418.     map->nummaterialstodraw += (map->drawmaterials[m] ? 1 : -1);
  419.     force_map_update(map);
  420. }
  421.  
  422. void
  423. toggle_map_aux_terrain(Map *map, int t)
  424. {
  425.     map->drawauxterrain[t] = !map->drawauxterrain[t];
  426.     force_map_update(map);
  427. }
  428.  
  429. void
  430. toggle_map_temperature(Map *map)
  431. {
  432.     map->drawtemperature = !map->drawtemperature;
  433.     force_map_update(map);
  434. }
  435.  
  436. void
  437. toggle_map_winds(Map *map)
  438. {
  439.     map->drawwinds = !map->drawwinds;
  440.     force_map_update(map);
  441. }
  442.  
  443. void
  444. toggle_map_clouds(Map *map)
  445. {
  446.     map->drawclouds = !map->drawclouds;
  447.     force_map_update(map);
  448. }
  449.  
  450. void
  451. toggle_map_storms(Map *map)
  452. {
  453.     map->drawstorms = !map->drawstorms;
  454.     force_map_update(map);
  455. }
  456.  
  457. void
  458. toggle_map_plans(Map *map)
  459. {
  460.     map->drawplans = !map->drawplans;
  461.     if (map->numselections > 0) {
  462.         force_map_update(map);
  463.     } else {
  464.         /* (should be a force update on control panel alone) */
  465.         draw_control_panel(map);
  466.     }
  467. }
  468.  
  469. void
  470. toggle_map_ai(Map *map)
  471. {
  472.     if (!side_has_ai(dside))
  473.       return;
  474.     map->drawai = !map->drawai;
  475.     force_map_update(map);
  476. }
  477.  
  478. static int selrect;
  479. static int downx, downy, downdir;
  480.  
  481. void drag_for_distance(Map *map, int h0, int v0);
  482.  
  483. void
  484. do_mouse_down_map_content(Map *map, int h, int v, int mods)
  485. {
  486.     int i, rslt, anysuccess;
  487.     Unit *unit;
  488.  
  489.     /* Remember this cell. */
  490.     m_nearest_cell(map, h, v, &downx, &downy);
  491.     /* Assume that last place clicked is a reasonable focus. */
  492.     if (inside_area(downx, downy)) {
  493.         map->vp->vcx = downx;  map->vp->vcy = downy;
  494.     }
  495.     if (map_modal > 0) {
  496.         switch (map_modal) {
  497.             case MOVE_TO_MODAL:
  498.                 do_move_to_command();
  499.                 /* Reset modality whether or not the command succeeded, otherwise
  500.                    the player can get caught here if no fire commands can succeed. */
  501.                 map_modal = NO_MODAL;
  502.                 break;
  503.             case FIRE_MODAL:
  504.                 do_fire_command();
  505.                 /* Reset modality whether or not the command succeeded, otherwise
  506.                    the player can get caught here if no fire commands can succeed. */
  507.                 map_modal = NO_MODAL;
  508.                 break;
  509.             case FIRE_INTO_MODAL:
  510.                 do_fire_into_command();
  511.                 /* Reset modality whether or not the command succeeded, otherwise
  512.                    the player can get caught here if no fire commands can succeed. */
  513.                 map_modal = NO_MODAL;
  514.                 break;
  515.             case SET_FORMATION_MODAL:
  516.                 do_set_formation();
  517.                 /* Reset modality whether or not the command succeeded, otherwise
  518.                    the player can get caught here if no commands can succeed. */
  519.                 map_modal = NO_MODAL;
  520.                 break;
  521.             case ADD_TERRAIN_MODAL:
  522.                 do_add_terrain();
  523.                 /* Reset modality whether or not the command succeeded, otherwise
  524.                    the player can get caught here if no commands can succeed. */
  525.                 map_modal = NO_MODAL;
  526.                 break;
  527.             case REMOVE_TERRAIN_MODAL:
  528.                 do_remove_terrain();
  529.                 /* Reset modality whether or not the command succeeded, otherwise
  530.                    the player can get caught here if no commands can succeed. */
  531.                 map_modal = NO_MODAL;
  532.                 break;
  533.             case DISTANCE_MODAL:
  534.                 drag_for_distance(map, h, v);
  535.                 /* Done being modal. */
  536.                 map_modal = NO_MODAL;
  537.                 break;
  538.             case ZOOM_MODAL:
  539.                 select_area_and_zoom(map, h, v, mods);
  540.                 /* Done being modal. */
  541.                 map_modal = NO_MODAL;
  542.                 break;
  543.             case GENERIC_MODAL:
  544.                 set_position_modally();
  545.                 map_modal = NO_MODAL;
  546.                 break;
  547.             default:
  548.                 /* (should error out) */
  549.                 break;
  550.         }
  551.     } else if (mods & cmdKey) {
  552.         if (map->moveonclick && map->autoselect) {
  553.             unselect_all(map);
  554.             m_nearest_unit(map, h, v, &unit);
  555.             if (unit != NULL && (side_controls_unit(dside, unit) || endofgame)) {
  556.                 /* The nearest unit will become the "current unit". */
  557.                 map->curunit = unit;
  558.                 select_unit_on_map(map, unit);
  559.                 draw_selections(map);
  560.                 move_on_drag(map, unit, mods);
  561.             } else {
  562.                 select_all_dragged_over(map, h, v, mods);
  563.                 /* Pick the first of the multiple selection as the "current unit". */
  564.                 if (map->numselections > 0) {
  565.                     map->curunit = map->selections[0];
  566.                 }
  567.             }
  568.         } else {
  569.             anysuccess = FALSE;
  570.             for (i = 0; i < map->numselections; ++i) {
  571.                 if ((unit = map->selections[i]) != NULL) {
  572.                     rslt = move_the_selected_unit(map, unit, h, v);
  573.                     if (rslt) anysuccess = TRUE;
  574.                 }
  575.             }
  576.             if (!anysuccess)
  577.               beep();
  578.         }
  579.     } else if (mods & optionKey) {
  580.         for (i = 0; i < map->numselections; ++i) {
  581.             if ((unit = map->selections[i]) != NULL) {
  582.                 fire_the_selected_unit(map, unit, h, v);
  583.             }
  584.         }
  585.     } else if (mods & shiftKey) {
  586.         m_nearest_unit(map, h, v, &unit);
  587.         if (unit && side_sees_unit(dside, unit)) {
  588.             /* Invert the selection status of the unit. */
  589.             if (unit_is_selected(map, unit)) {
  590.                 unselect_unit_on_map(map, unit);
  591.                 erase_selection(map, unit);
  592.             } else {
  593.                 select_unit_on_map(map, unit);
  594.                 draw_selections_at(map, unit->x, unit->y);
  595.             }
  596.         } else {
  597.             select_all_dragged_over(map, h, v, mods);
  598.         }
  599.     } else {
  600.         /* Interpret an unmodified mouse down. */
  601. #ifdef DESIGNERS
  602.         if (dside->designer && tooltype != notool) {
  603.             apply_designer_tool(map, h, v, mods);
  604.         } else
  605. #endif /* DESIGNERS */
  606.         if (map->moveonclick
  607. #ifdef DESIGNERS
  608.             && !dside->designer
  609. #endif /* DESIGNERS */
  610.             ) {
  611.             /* Usually will only be one to move, but be general anyway. */
  612.             for (i = 0; i < map->numselections; ++i) {
  613.                 if ((unit = map->selections[i]) != NULL) {
  614.                     move_the_selected_unit(map, unit, h, v);
  615.                 }
  616.             }
  617.             map->scrolltocurunit = TRUE;
  618.         } else {
  619.             unselect_all(map);
  620.             m_nearest_unit(map, h, v, &unit);
  621.             if (unit != NULL && (side_controls_unit(dside, unit) || endofgame)) {
  622.                 select_unit_on_map(map, unit);
  623.                 draw_selections(map);
  624.                 move_on_drag(map, unit, mods);
  625.             } else {
  626.                 select_all_dragged_over(map, h, v, mods);
  627.             }
  628.         }
  629.     }
  630. }
  631.  
  632. void
  633. select_all_dragged_over(Map *map, int h0, int v0, int mods)
  634. {
  635.     Point pt0, pt1, newmouse;
  636.     int drawn = FALSE;
  637.     Rect tmprect;
  638.  
  639.     SetPt(&pt0, h0, v0);
  640.     SetPt(&pt1, h0, v0);
  641.     SetRect(&tmprect, h0, v0, h0, v0);
  642.     /* (should be a generic subr?) */
  643.     PenMode(patXor);
  644.     PenPat(QDPat(gray));
  645.     while (WaitMouseUp()) {
  646.         GetMouse(&newmouse);
  647.         if (!EqualPt(pt1, newmouse) /* && PtInRect(newmouse, &(map->window->portRect)) */) {
  648.             if (drawn) {
  649.                 tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
  650.                 tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
  651.                 FrameRect(&tmprect);
  652.             }
  653.             pt1 = newmouse;
  654.             tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
  655.             tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
  656.             FrameRect(&tmprect);
  657.             drawn = TRUE;
  658.         }
  659.     }
  660.     if (drawn) {
  661.         tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
  662.         tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
  663.         FrameRect(&tmprect);
  664.     }
  665.     PenNormal();
  666.     select_all_units_in_rect(map, &tmprect);
  667. }
  668.  
  669. void
  670. select_area_and_zoom(Map *map, int h0, int v0, int mods)
  671. {
  672.     Point pt0, pt1, newmouse;
  673.     int drawn = FALSE, x, y;
  674.     Rect tmprect;
  675.  
  676.     SetPt(&pt0, h0, v0);
  677.     SetPt(&pt1, h0, v0);
  678.     /* (should be a generic subr) */
  679.     PenMode(patXor);
  680.     PenPat(QDPat(gray));
  681.     while (WaitMouseUp()) {
  682.         GetMouse(&newmouse);
  683.         if (!EqualPt(pt1, newmouse) /* && PtInRect(newmouse, &(map->window->portRect)) */) {
  684.             if (drawn) {
  685.                 tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
  686.                 tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
  687.                 FrameRect(&tmprect);
  688.             }
  689.             pt1 = newmouse;
  690.             tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
  691.             tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
  692.             FrameRect(&tmprect);
  693.             drawn = TRUE;
  694.         }
  695.     }
  696.     if (drawn) {
  697.         tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
  698.         tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
  699.         FrameRect(&tmprect);
  700.     }
  701.     PenNormal();
  702.     m_nearest_cell(map, pt1.h, pt1.v, &x, &y);
  703.     if (x != downx && y != downy) {
  704.         magnify_to_fit(map, downx, downy, x, y);
  705.     }
  706. }
  707.  
  708. void
  709. move_on_drag(Map *map, Unit *unit, int mods)
  710. {
  711.     int sx, sy, sw, sh, h0, v0, drawn = FALSE, x, y;
  712.     Point pt0, pt1, newmouse;
  713.  
  714.     m_xform_unit_self(map, unit, &sx, &sy, &sw, &sh);
  715.     h0 = sx + sw / 2;  v0 = sy + sh / 2;
  716.     SetPt(&pt0, h0, v0);
  717.     SetPt(&pt1, h0, v0);
  718.     /* (should be a generic subr?) */
  719.     PenMode(patXor);
  720.     while (WaitMouseUp()) {
  721.         GetMouse(&newmouse);
  722.         /* should scroll, then abort if we drag outside the window */
  723.         if (0 /* PtInRect(newmouse, &(map->window->portRect)) */) {
  724.         }
  725.         if (!EqualPt(pt1, newmouse)) {
  726.             if (drawn) {
  727.                 MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
  728.             }
  729.             pt1 = newmouse;
  730.             MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
  731.             drawn = TRUE;
  732.         }
  733.     }
  734.     /* Erase the last drawn line. */
  735.     if (drawn) {
  736.         MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
  737.     }
  738.     PenNormal();
  739.     m_nearest_cell(map, pt1.h, pt1.v, &x, &y);
  740.     if (x != downx || y != downy) {
  741.         if (!move_the_selected_unit(map, unit, pt1.h, pt1.v))
  742.           beep();
  743.     } else {
  744.         /* (should try to enter another unit in this cell) */
  745.     }
  746. }
  747.  
  748. void
  749. drag_for_distance(Map *map, int h0, int v0)
  750. {
  751.     Point pt0, pt1, newmouse;
  752.     int drawn = FALSE, x, y;
  753.  
  754.     SetPt(&pt0, h0, v0);
  755.     SetPt(&pt1, h0, v0);
  756.     /* (should be a generic subr) */
  757.     PenMode(patXor);
  758.     PenPat(QDPat(gray));
  759.     while (WaitMouseUp()) {
  760.         GetMouse(&newmouse);
  761.         /* should scroll, then abort if we drag outside the window */
  762.         if (0 /* PtInRect(newmouse, &(map->window->portRect)) */) {
  763.         }
  764.         if (!EqualPt(pt1, newmouse)) {
  765.             if (drawn) {
  766.                 MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
  767.             }
  768.             pt1 = newmouse;
  769.             MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
  770.             drawn = TRUE;
  771.         }
  772.     }
  773.     /* Erase the last drawn line. */
  774.     if (drawn) {
  775.         MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
  776.     }
  777.     PenNormal();
  778.     m_nearest_cell(map, pt1.h, pt1.v, &x, &y);
  779.     notify(dside, "Distance from %d,%d to %d,%d is %d",
  780.            downx, downy, x, y, distance(downx, downy, x, y));
  781. }
  782.  
  783. void
  784. unselect_all(Map *map)
  785. {
  786.     int num = map->numselections;
  787.  
  788.     if (map->numselections > 0) {
  789.         erase_selections(map);
  790.         map->numselections = 0;
  791.     }
  792. }
  793.  
  794. /* Add the given unit to the array of units selected in the given map.  If we need
  795.    more space, then grow the array by 50%. */
  796.  
  797. void
  798. select_unit_on_map(Map *map, Unit *unit)
  799. {
  800.     if (map->numselections >= map->maxselections) {
  801.         int newsize = map->maxselections + map->maxselections / 2;
  802.         Unit **newarray = (Unit **) realloc((char *) map->selections, newsize * sizeof(Unit *));
  803.  
  804.         if (newarray == NULL) {
  805.             run_warning("couldn't realloc map selection array");
  806.             return;
  807.         }
  808.         map->maxselections = newsize;
  809.         map->selections = newarray;
  810.     }
  811.     map->selections[map->numselections++] = unit;
  812. }
  813.  
  814. int
  815. unit_is_selected(Map *map, Unit *unit)
  816. {
  817.     int i;
  818.  
  819.     for (i = 0; i < map->numselections; ++i) {
  820.         if (map->selections[i] == unit) return TRUE;
  821.     }
  822.     return FALSE;
  823. }
  824.  
  825. void
  826. unselect_unit_on_map(Map *map, Unit *unit)
  827. {
  828.     int i, j;
  829.  
  830.     for (i = 0; i < map->numselections; ++i) {
  831.         if (map->selections[i] == unit) {
  832.             /* Keep selection list contiguous, move other units down. */
  833.             for (j = i + 1; j < map->numselections; ++j) {
  834.                 map->selections[j - 1] = map->selections[j];
  835.             }
  836.             --map->numselections;
  837.             return;
  838.         }
  839.     }
  840. }
  841.  
  842. /* Given a map and a rectangle in it, select all the units whose images touch on
  843.    that rectangle. */
  844.  
  845. void
  846. select_all_units_in_rect(Map *map, Rect *rectptr)
  847. {
  848.     int rectissmall = FALSE;
  849.     int sx, sy, sw, sh;
  850.     Unit *unit;
  851.     Rect unitrect, tmprect;
  852.     
  853.     /* First see if we're selecting over a large area or within a single cell. */
  854.     if (rectptr->right - rectptr->left < map->vp->hw
  855.         && rectptr->bottom - rectptr->top < map->vp->hh) rectissmall = TRUE;
  856.     /* Now look at all the plausible units and see if any's image intersects the rect. */
  857.     for_all_units(unit) {
  858.         if (in_play(unit)
  859.             && (side_controls_unit(dside, unit) || endofgame)
  860.             && (rectissmall || unit->transport == NULL)) {
  861.             m_xform_unit_self(map, unit, &sx, &sy, &sw, &sh);
  862.             SetRect(&unitrect, sx, sy, sx + sw, sy + sh);
  863.             if (SectRect(&unitrect, rectptr, &tmprect)) {
  864.                 select_unit_on_map(map, unit);
  865.                 /* (could do setport etc once...) */
  866.                 draw_selected_unit_setport(map, unit);
  867.             }
  868.         }
  869.     }
  870. }
  871.  
  872. /* This translates the user's "go to here" into appropriate tasks and/or actions. */
  873.  
  874. int
  875. move_the_selected_unit(Map *map, Unit *unit, int h, int v)
  876. {
  877.     int x, y, rslt;
  878.     Unit *other = NULL;
  879.  
  880.     m_nearest_cell(map, h, v, &x, &y);
  881.     if (unit_at(x, y) != NULL) {
  882.         m_nearest_unit(map, h, v, &other);
  883.     }
  884.     rslt = advance_into_cell(dside, unit, x, y, other);
  885.     return rslt;
  886. }
  887.  
  888. void
  889. fire_the_selected_unit(Map *map, Unit *unit, int h, int v)
  890. {
  891.     int x, y;
  892.     Unit *other;
  893.  
  894.     m_nearest_cell(map, h, v, &x, &y);
  895.     if (x != unit->x || y != unit->y) {
  896.         if (unit->act && unit->plan) { /* (should be more sophisticated test?) */
  897.             if ((other = unit_at(x, y)) != NULL) {
  898.                 /* There's a unit to fire at. */
  899.                 if (other->side == unit->side) {
  900.                     beep();
  901.                 } else {
  902.                     prep_fire_at_action(unit, unit, other, -1);
  903.                 }
  904.             } else {
  905.                 beep();
  906.             }
  907.         }
  908.     }
  909. }
  910.  
  911. void
  912. select_exactly_one_unit(Map *map, Unit *unit)
  913. {
  914.     Unit *thisunit;
  915.  
  916.     if (map->numselections > 0) {        
  917.         thisunit = map->selections[0];
  918.         if (thisunit == unit) return;
  919.     }
  920.     unselect_all(map);
  921.     select_unit_on_map(map, unit);
  922.     scroll_to_unit(map, unit);
  923.     draw_selections(map);
  924. }
  925.  
  926. void
  927. select_next_unit(Map *map)
  928. {
  929.     select_another(map, find_next_unit);
  930. }
  931.  
  932. void
  933. select_previous_unit(Map *map)
  934. {
  935.     select_another(map, find_prev_unit);
  936. }
  937.  
  938. void
  939. select_next_actor(Map *map)
  940. {
  941.     select_another(map, find_next_actor);
  942. }
  943.  
  944. void
  945. select_previous_actor(Map *map)
  946. {
  947.     select_another(map, find_prev_actor);
  948. }
  949.  
  950. void
  951. select_next_mover(Map *map)
  952. {
  953.     select_another(map, find_next_mover);
  954. }
  955.  
  956. void
  957. select_previous_mover(Map *map)
  958. {
  959.     select_another(map, find_prev_mover);
  960. }
  961.  
  962. void
  963. select_next_awake_mover(Map *map)
  964. {
  965.     select_another(map, find_next_awake_mover);
  966. }
  967.  
  968. void
  969. select_previous_awake_mover(Map *map)
  970. {
  971.     select_another(map, find_prev_awake_mover);
  972. }
  973.  
  974. /* Given a map and a searching function, go find the "next" matching unit and select it. */
  975.  
  976. void
  977. select_another(Map *map, Unit *(*fn)(Side *side, Unit *unit))
  978. {
  979.     Unit *thisunit, *nextunit;
  980.  
  981.     if (fn == NULL) {
  982.         beep();
  983.         return;
  984.     }
  985.     if (map->numselections > 0) {
  986.         thisunit = map->selections[0];
  987.     } else {
  988.         thisunit = NULL;
  989.     }
  990.     nextunit = (*fn)(dside, thisunit);
  991.     if (nextunit != NULL) {
  992.         unselect_all(map);
  993.         select_unit_on_map(map, nextunit);
  994.         scroll_to_unit(map, nextunit);
  995.         draw_selections(map);
  996.         if (map->autoselect) {
  997.             map->curunit = nextunit;
  998.         }
  999.     } else if (thisunit != NULL) {
  1000.         scroll_to_unit(map, thisunit);
  1001.         /* (should not be done this way, but how else?) */
  1002.         if (map->autoselect
  1003.             && (thisunit->act && thisunit->act->acp > 0)
  1004.             && (thisunit->plan && !thisunit->plan->asleep && !thisunit->plan->reserve && !thisunit->plan->delayed)) {
  1005.             map->curunit = thisunit;
  1006.         }
  1007.     }
  1008. }
  1009.  
  1010. void
  1011. scroll_best_map_to_unit(Unit *unit)
  1012. {
  1013.     Map *map, *bestmap;
  1014.  
  1015.     /* Find the "best" (highest power, unit already visible) map to scroll over
  1016.        to the unit. */
  1017.     bestmap = maplist;
  1018.     for_all_maps(map) {
  1019.         if (map->vp->power > bestmap->vp->power
  1020.             || (map->vp->power == bestmap->vp->power
  1021.                 && in_middle(map, unit->x, unit->y)
  1022.                  && !in_middle(bestmap, unit->x, unit->y))) {
  1023.             bestmap = map;
  1024.         }
  1025.     }
  1026.     /* We have a map, now make it show the unit. */
  1027.     if (!in_middle(bestmap, unit->x, unit->y)) {
  1028.         scroll_to_unit(bestmap, unit);
  1029.     }
  1030.     SelectWindow(bestmap->window);
  1031.     adjust_menus();
  1032. }
  1033.  
  1034. /* Scroll the given map over to display the given unit. */
  1035.  
  1036. void
  1037. scroll_to_unit(Map *map, Unit *unit)
  1038. {
  1039.     if (inside_area(unit->x, unit->y)) {
  1040.         if (!in_middle(map, unit->x, unit->y)) {
  1041.             set_view_focus(map->vp, unit->x, unit->y);
  1042.             m_center_on_focus(map);
  1043.             set_map_scrollbars(map);
  1044.             force_map_update(map);
  1045.         }
  1046.     }
  1047. }
  1048.  
  1049. /* This routine changes a map's viewport and magnification to fit the given rectangle. */
  1050.  
  1051. void
  1052. magnify_to_fit(Map *map, int x1, int y1, int x2, int y2)
  1053. {
  1054.     int wid, hgt, wanted, power;
  1055.  
  1056.     DGprintf("Magnifying map to fit in area %d,%d - %d,%d\n", x1, y1, x2, y2);
  1057.     /* (still need to do y/2 correction) */
  1058.     wid = abs(x2 - x1) + 1;  hgt = abs(y2 - y1) + 1;
  1059.     map->vp->vcx = min(x1, x2) + wid / 2;  map->vp->vcy = min(y1, y2) + hgt / 2;
  1060.     /* Compute the "ideal" size of a displayed cell. */
  1061.     wanted = min(map->vp->pxw / wid, map->vp->pxh / hgt);
  1062.     /* Search for the best approximation. */
  1063.     for (power = NUMPOWERS-1; power > 0; --power) {
  1064.         if (hws[power] < wanted) break;
  1065.     }
  1066.     set_map_mag(map, power);
  1067. }
  1068.